home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 25
/
Aminet 25 (1998)(GTI - Schatztruhe)[!][Jun 1998].iso
/
Aminet
/
util
/
arc
/
mpackWOS.lha
/
mpackppc
/
src
/
macnsmtp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-04-08
|
12KB
|
411 lines
/* macnsmtp.c -- simple async SMTP client
*/
/* (C) Copyright 1995 by Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of Carnegie
* Mellon University not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. Carnegie Mellon University makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/* (C) Copyright 1994-1995 by Christopher J. Newman
* All Rights Reserved.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Christopher J. Newman not be used in
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. Christopher J. Newman makes no
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* CHRISTOPHER J. NEWMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
* SHALL CHRISTOPHER J. NEWMAN BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*
* Author: Christopher J. Newman
* Message: This is a nifty program.
*/
#include "macnapp.h"
#define SMTP_PORT 25
typedef struct {
na_win w;
void *context; /* user context */
short num, rcpt; /* recipient count (num), and sent (rcpt) */
na_smtpstat statf; /* callback */
na_tcp tcpid; /* TCP id */
short state; /* SMTP state (see below) */
short count; /* bytes used in linebuf */
short refnum; /* input file */
short crfound:1; /* found a CR in SMTP server data */
short crlf:1; /* found a CRLF in SMTP server data */
short crtrans:1; /* translate CR to CRLF in file */
long headsize; /* size of extra headers starting at data */
long fsize, fdone; /* file size & amount written */
long bufsize; /* usable buffer size */
Ptr buf; /* output buffer */
char linebuf[1024]; /* input line buffer */
char data[1]; /* header & envelope */
} na_smtpwin;
#define sw ((na_smtpwin *) w)
/* states: */
#define S_conn 0 /* connecting to server */
#define S_greet 1 /* waiting for greeting */
#define S_hello 2 /* waiting for local host lookup and HELO reply */
#define S_mailf 3 /* waiting for MAIL FROM reply */
#define S_rcpt 4 /* waiting for RCPT TO reply */
#define S_data 5 /* waiting for DATA continue reply */
#define S_send 6 /* transmitting data */
#define S_done 7 /* waiting for DATA success reply */
#define S_quit 8 /* waiting for QUIT reply */
#define S_wait 9 /* waiting for connection close */
#define S_close 10 /* closed */
/* generate and submit SMTP command line (put command & data together with CRLF ending)
* returns NATCPwrite result code
*/
static int SMTPsend(na_win *w, char *com, char *data)
{
char buf[512];
char *dest = buf;
int result = 0;
while ((*dest = *com) != '\0') ++dest, ++com;
if (data) {
while ((*dest = *data++) != '\0') ++dest;
if (com[-1] == '<') *dest++ = '>';
}
*dest++ = '\r';
*dest++ = '\n';
result = NATCPwrite(sw->tcpid, buf, dest - buf, -1);
return (result);
}
/* do final callback
*/
static void smtpclose(na_win *w, short code, short err, long size, char *data)
{
if (sw->state < S_wait) {
NATCPclose(sw->tcpid);
FSClose(sw->refnum);
sw->state = S_wait;
(*sw->statf)(sw->context, code, err, size, data);
}
}
/* TCP read/write callback
*/
static void readp(void *wh, na_tcp s, short status, long size, char *data)
{
na_win *w, **taskh;
char *dest;
short major, count, smtpcode;
/* make sure our SMTP task still exists */
for (taskh = NAtask; taskh && taskh != wh; taskh = (*taskh)->task);
if (!taskh) return;
/* handle TCP result */
w = NAlockWindow((na_win **) wh);
if (status == NATCP_connect) {
/* deal with new connection */
sw->state = S_greet;
} else if (status < 0) {
/* deal with TCP errors */
smtpclose(w, NASMTP_tcpfail, status, size, NULL);
if (status == NATCP_closed) sw->state = S_close;
} else if (status & NATCP_closing) {
/* deal with a closed connection */
if (sw->state < S_wait) {
smtpclose(w, NASMTP_conclosed, 0, 0, NULL);
}
} else if (status & NATCP_data) {
do {
/* buffer SMTP line */
dest = sw->linebuf + sw->count;
while (size && sw->count < sizeof (sw->linebuf)) {
--size, ++sw->count;
if (sw->crfound && *data == '\n') {
*--dest = '\0';
--sw->count;
++data;
sw->crfound = 0;
sw->crlf = 1;
break;
}
sw->crfound = (*dest++ = *data++) == '\r';
}
if (!sw->crlf) {
if (sw->count == sizeof (sw->linebuf)) {
sw->linebuf[sw->count - 1] = '\0';
smtpclose(w, NASMTP_badprot, 0, 0, sw->linebuf);
}
break;
}
sw->crlf = 0;
/* parse SMTP result code */
dest = sw->linebuf;
if (sw->count < 3 || !isdigit(dest[0])
|| !isdigit(dest[1]) || !isdigit(dest[2])) {
smtpclose(w, NASMTP_badprot, 0, 0, dest);
break;
}
sw->count = 0;
major = dest[0] - '0';
smtpcode = major * 100 + (dest[1] - '0') * 10 + (dest[2] - '0');
/* handle reply continuation */
if (dest[3] == '-') continue;
/* handle major errors */
if (major > 3) {
if (sw->state != S_rcpt) {
smtpclose(w, major == 4 ? NASMTP_temperr : NASMTP_permerr,
smtpcode, sw->state, dest + 3);
break;
}
(*sw->statf)(sw->context, NASMTP_badaddr, smtpcode, 0, sw->linebuf + 3);
}
dest = sw->data + sw->headsize;
/* state changes */
switch (sw->state) {
case S_greet:
if (sw->buf) {
SMTPsend(w, "HELO ", sw->buf);
if (sw->buf) DisposPtr(sw->buf);
}
sw->state = S_hello;
break;
case S_hello:
SMTPsend(w, "MAIL FROM:<", dest);
(*sw->statf)(sw->context, NASMTP_progress, 5, 0, 0);
sw->state = S_mailf;
break;
case S_mailf:
case S_rcpt:
count = ++sw->rcpt;
if (count < sw->num + 1) {
while (count--) {
while (*dest++);
}
SMTPsend(w, "RCPT TO:<", dest);
(*sw->statf)(sw->context, NASMTP_progress, 5 + 10 * sw->rcpt / sw->num, 0, 0);
} else {
sw->state = S_data;
SMTPsend(w, "DATA", 0);
(*sw->statf)(sw->context, NASMTP_progress, 20, 0, 0);
}
break;
case S_data:
if (major != 3) {
smtpclose(w, NASMTP_badprot, 0, 0, dest);
break;
}
sw->state = S_send;
if (sw->headsize) {
sw->buf = NewPtr(sw->bufsize = sw->headsize);
if (!sw->buf) {
smtpclose(w, NASMTP_nomem, 0, 0, NULL);
break;
}
memcpy(sw->buf, sw->data, sw->headsize);
}
case S_send:
/* NOTE: this case should never happen */
break;
case S_done:
sw->state = S_quit;
SMTPsend(w, "QUIT", NULL);
(*sw->statf)(sw->context, NASMTP_progress, 95, 0, 0);
break;
case S_quit:
smtpclose(w, NASMTP_completed, 0, 0, 0);
break;
}
} while (size);
}
NAunlockWindowh((na_win **) wh, w)
}
/* TCP gethost callback
*/
static void hostp(void *wh, na_tcp s, short status, long size, char *data)
{
na_win *w, **taskh;
/* make sure our task still exists */
for (taskh = NAtask; taskh && taskh != wh; taskh = (*taskh)->task);
if (!taskh) return;
/* store host/error */
w = NAlockWindow((na_win **) wh);
if (status == NATCP_connect) {
if (sw->state == S_hello) {
SMTPsend(w, "HELO ", data);
} else {
sw->buf = NewPtr(size + 1);
if (sw->buf == NULL) {
smtpclose(w, NASMTP_nomem, 0, 0, NULL);
} else {
memcpy(sw->buf, data, size + 1);
}
}
} else {
smtpclose(w, NASMTP_tcpfail, status, size, NULL);
}
NAunlockWindowh((na_win **) wh, w);
}
/* translate CR to CRLF
*/
static void crtocrlf(char *buf, long *size)
{
long crcount = 0;
char *src, *dst, *end = buf + *size;
for (src = buf; src < end; ++src) {
if (src[0] == '\r') ++crcount;
}
src = end - 1;
for (dst = src + crcount; dst > src; *dst-- = *src--) {
if (*src == '\r') *dst-- = '\n';
}
*size += crcount;
}
/* SMTP task
*/
static short taskp(na_win *w)
{
OSErr oserr;
if (sw->state == S_send || sw->state == S_done) {
/*XXX: could be generous with NewPtr() failure if a NATCPwritePending() */
if (!sw->bufsize) {
if ((sw->buf = NewPtr(8192)) == NULL) {
smtpclose(w, NASMTP_nomem, 0, 0, NULL);
return (NA_NOTPROCESSED);
}
sw->bufsize = sw->crtrans ? 4096 : 8192;
oserr = FSRead(sw->refnum, &sw->bufsize, sw->buf);
if (oserr != noErr && oserr != eofErr) sw->bufsize = 0;
if (!sw->bufsize) {
if (oserr == eofErr && sw->state == S_send) {
memcpy(sw->buf, "\r\n.\r\n", 5);
sw->bufsize = 5;
sw->state = S_done;
} else {
DisposPtr(sw->buf);
}
} else {
if (sw->crtrans) {
crtocrlf(sw->buf, &sw->bufsize);
}
(*sw->statf)(sw->context, NASMTP_progress, 20
+ 70 * (sw->fdone += sw->bufsize) / sw->fsize, 0, 0);
}
}
if (sw->bufsize && NATCPwrite(sw->tcpid, sw->buf, sw->bufsize, 1) == NATCP_data) {
sw->bufsize = 0;
}
}
return (sw->state == S_close ? NA_REQCLOSE : NA_NOTPROCESSED);
}
/* SMTP close procedure
* IMPORTANT: if the user quits during mail transmission, we want to
* warn the user that mail will be lost.
*/
static short closep(na_win *w)
{
if (sw->state < S_wait) {
/*XXX: put modal dialog here, allow abort of close */
if (sw->tcpid >= 0) NATCPclose(sw->tcpid);
FSClose(sw->refnum);
}
return (NA_CLOSED);
}
/* submit file to SMTP:
* creates SMTP task, initializes data, starts TCP connection
* copies from & dest addresses, so they don't need to persist
* task is not completed until statf() is called
*/
void NASMTPsubmit(na_smtpstat statf, char *server, FSSpec *fspec, Handle headers,
Handle envelope, short flags, void *context)
{
long size;
na_win **wh, *w;
char *src, *dst;
OSErr oserr;
/* create task */
size = sizeof (na_smtpwin);
if (headers) size += GetHandleSize(headers);
size += GetHandleSize(envelope);
wh = NAaddtask(taskp, size);
if (!wh) {
(*statf)(context, NASMTP_nomem, 0, 0, 0);
return;
}
/* init task */
w = NAlockWindow(wh);
w->type = NA_SMTPTYPE;
w->closep = closep;
sw->context = context;
sw->statf = statf;
if (headers && (sw->headsize = GetHandleSize(headers))) {
memcpy(sw->data, (char *) *headers, sw->headsize);
}
size = GetHandleSize(envelope);
sw->num = -1;
dst = sw->data + sw->headsize;
for (src = (char *) *envelope; size; --size) {
if ((*dst++ = *src++) == '\0') ++sw->num;
}
if (flags & NASMTP_crtrans) sw->crtrans = 1;
/* open file */
if ((oserr = HOpen(fspec->vRefNum, fspec->parID, fspec->name,
fsRdPerm, &sw->refnum)) != noErr) {
(*statf)(context, NASMTP_oserr, 0, oserr, 0);
NAcloseWindow(w, NA_CLOSED);
return;
}
GetEOF(sw->refnum, &sw->fsize);
/* open MacTCP */
sw->tcpid = NATCPopen(readp, (void *) wh, server, SMTP_PORT, 0);
if (sw->tcpid != -1) NATCPgethost(hostp, (void *) wh);
NAunlockWindowh(wh, w);
}